package cn.ratel.kit;

import cn.ratel.cache.redis.JedisKit;

public class RedisLockKit {
	private static final int DEFAULT_TIMEOUT = 1000;
	private static final int RETRY_PERIOD = 100;

	public static boolean acquire(String lockKey) throws InterruptedException {
		return tryAcquire(lockKey, DEFAULT_TIMEOUT, 0);
	}

	public static boolean acquire(String lockKey, int timeout) throws InterruptedException {
		return tryAcquire(lockKey, timeout, 0);
	}

	public static boolean acquire(String lockKey, int timeout, int retryTimes) throws InterruptedException {
		return tryAcquire(lockKey, timeout, retryTimes);
	}

	private static boolean tryAcquire(final String lockKey, int timeout, int retryTimes) throws InterruptedException {
		synchronized (lockKey) {
			int expire = retryTimes * RETRY_PERIOD;
			while (timeout >= 0) {
				String newExpireAt = System.currentTimeMillis() + expire + 1 + "";

				if (JedisKit.call(e -> e.setnx(lockKey, newExpireAt)) == 1) {
					return true;
				} else if (retryTimes == 0) {
					return false;
				}

				String currentExpireAt = JedisKit.call(e -> e.get(lockKey));
				if (currentExpireAt != null && Long.valueOf(currentExpireAt) < System.currentTimeMillis()) {
					// 已经超时，视为锁未能释放，再次尝试获取
					String oldExpireAt = JedisKit.call(e -> e.getSet(lockKey, newExpireAt));
					if (oldExpireAt != null && oldExpireAt.equals(currentExpireAt)) {
						return true;
					}
				}
				timeout -= RETRY_PERIOD;
				Thread.sleep(RETRY_PERIOD);
			}
			return false;
		}
	}

	public static void release(String lockKey) {
		JedisKit.call(e -> e.del(lockKey));
	}
}
